package edu.gatech.oad.mule;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

import edu.gatech.oad.mule.config.PlayerData;

/**
 * This class is the main GUI for the configuration screen.  It will handle 
 * gathering and validating user input, and pass the result over to 
 * GameDriver to start setting up the game.
 * 
 * @author Brendan McGarry, Dale Avery, D'Miria Collins, Natasha Parekh, Jaemin Park
 * @version %I%, %G%
 */
public class MiddleConfigPanel extends JPanel{

	//how "close" two colors can be to each other.  The smaller
	//  this constant, the more close the colors can be.
	//  The "closeness" is calculated by graphing the RGB
	//  values of the colors in R^3 and calculating the distance
	//  between all the colors.
	public static final double COLOR_PROXIMITY_CONST = 75.0;

	//columns for the main table of player data
	private String[] columns = {"#", "Name", "Race", "Color", "Input Device"};

	//all the form information for the players:
	private JComponent[][] playerData = 
		new JComponent[ConfigurationPanel.MAX_PLAYERS + 1][columns.length];

	//spacing around componenets
	private int padding = 3;

	//instance of the overall panel
	private ConfigurationPanel cp;

	//random object used for making random colors
	private Random r = new Random();

	/**
	 * This constructor creates a new MiddleConfigPanel
	 */
	public MiddleConfigPanel(ConfigurationPanel cp){
		System.out.println("middleConfigPanel");
		//store the instance of the bigger container
		this.cp = cp;

		//give this guy a border so we have a little breathing room
		//  from the other JPanels
		setBorder(new EmptyBorder(
				ConfigurationPanel.PANEL_PADDING,
				ConfigurationPanel.PANEL_PADDING,
				ConfigurationPanel.PANEL_PADDING,
				ConfigurationPanel.PANEL_PADDING
				));

		//since this is basically a table, we are going to
		//  use the GridBagLayout to make everything look
		//  pretty.  It's like GridLayout, but we can have
		//  columns be unequal widths
		setLayout(new GridBagLayout());
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.fill = GridBagConstraints.NONE;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.insets = new Insets(padding, padding, padding, padding);

		//populate the table with the column headings
		for(int i = 0; i < columns.length; i++){
			playerData[0][i] = new JLabel(columns[i]);
		}//end for

		//populate the player info stuff
		for(int i = 1; i <= ConfigurationPanel.MAX_PLAYERS; i++){
			JComponent[] row = new JComponent[columns.length];
			//row #
			row[0] = new JLabel("" + i + ")");
			//player name textbox
			row[1] = new JTextField("Player " + i + "", 10);
			//dropdown for races
			row[2] = new JComboBox(ConfigurationPanel.RACES);
			//picking player color (use a randomly generated color)
			row[3] = new JPanel();
			row[3].setPreferredSize(new Dimension(20, 20));
			row[3].setBackground(new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256)));
			row[3].addMouseListener(new ColorChangeListener(i));
			//input device they want to use
			row[4] = new JComboBox(ConfigurationPanel.INPUT_DEVICES);
			playerData[i] = row;
		}//end for

		//add everything from playerData
		for(int i = 0; i <= ConfigurationPanel.MAX_PLAYERS; i++){
			for(int j = 0; j < columns.length; j++){
				gbc.gridx = j;
				gbc.gridy = i;
				add(playerData[i][j], gbc);
			}//end for
		}//end for

		//call enable on the commponents
		enableComponents(ConfigurationPanel.MIN_PLAYERS);
		System.out.println("/middleConfigPanel");
	}//end constructor

	private class ColorChangeListener extends MouseAdapter{
		
		private int code;

		public ColorChangeListener(int code){
			this.code = code;
		}//end constructor

		public void mousePressed(MouseEvent me){
			JPanel jp = (JPanel)me.getSource();
			if(code <= cp.getNumberOfPlayers()){
				Color c = JColorChooser.showDialog(null, "Choose Your Color: ", Color.RED);
				if(c != null){
					jp.setBackground(c);
				}//end if
			}//end if
		}//end method mousePressed
	}//end class ColorChangeListener

	/**
	 * Uses the distance formula for R3 to find the "distance" between to colors.
	 *   It treats R, G, and B values as x, y, and z to create a two points in 
	 *   R3 and then calculate the distance.
	 *
	 * @param c1 The first color
	 * @param c2 The second color
	 * @return The "distance" between the two colors
	 */
	private double distance(Color c1, Color c2){
		System.out.println("distance");
		System.out.println("/distance");
		//at least one of the colors is null
		if(c1 == null | c2 == null){
			//treat them as the same color
			return 0.0;
		}//end if
		else{
			return Math.sqrt(square(c2.getRed() - c1.getRed()) + square(c2.getGreen() - c1.getGreen()) + square(c2.getBlue() - c1.getBlue()));
		}//end else
	}//end method distance

	/**
	 * This method disables the user from editing some of the components
	 * in the middle panel
	 *
	 * @param howMany The number of components to enable
	 */
	public void enableComponents(int howMany){
		System.out.println("enableComponents");
		//go through all the GUI components the user
		//  can edit.  we skip the 0th row because it 
		//  just has labels
		for(int i = 1; i <= ConfigurationPanel.MAX_PLAYERS; i++){
			//every column
			for(int j = 0; j < columns.length; j++){
				//enable the components in the first
				//  howMany rows
				if(i <= howMany){	
					playerData[i][j].setEnabled(true);
				}//end if
				//disable everyone else
				else{
					playerData[i][j].setEnabled(false);
				}//end else
			}//end for
		}//end outerfor
		System.out.println("/enableComponents");
	}//end method disableComponents

	/**
	 * @return An array of the player's configuration data (color, name, etc).
	 * row 0 corresponds to the player 1's data, row 1 corresponds to player 2's
	 * data, etc.  The table structure looks like this:
	 *
	 * p1.playerData = p1.name	p1.race		p1.color	p1.input_device
	 * p2.playerData = p2.name	p2.race		p2.color	p2.input_device
	 * p3.playerData = p3.name	p3.race		p3.color	p3.input_device
	 * ...		...		...		...
	 * pn.playerData = pn.name	pn.race		pn.color	pn.input_device
	 *
	 */
	public PlayerData[] getPlayerData(int numPlayers){
		System.out.println("getPlayerData");
		//create a return matrix with the number of rows equal to the number
		//  of active players and the number of columns one less than the original
		//  matrix (since the first column in the matrix is just the player # and is
		//  trivial)
		PlayerData[] returnData = new PlayerData[numPlayers];
		//start in the first row of playerData (since the 0th row is just
		//  headings)
		for(int i = 1; i <= numPlayers; i++){
			returnData[i-1] = new PlayerData();
			//player name textboxs
			returnData[i-1].setName(((JTextField)(playerData[i][1])).getText().trim());
			//dropdown for races
			returnData[i-1].setRace((String)(((JComboBox)playerData[i][2]).getSelectedItem()));
			//picking player color (use a randomly generated color)
			returnData[i-1].setColor(playerData[i][3].getBackground());
			//input device they want to use
			returnData[i-1].setInputDevice((String)(((JComboBox)playerData[i][4]).getSelectedItem()));
		}//end outer for
		System.out.println("/getPlayerData");
		return returnData;
	}//end method getPlayerData

	/**
	 * Takes an integer and raises it to the second power
	 *
	 * @param value The integer value to be square1d
	 * @return (value)^2
	 */
	private int square(int value){
		System.out.println("square");
		System.out.println("/square");
		return (value * value);
	}//end method

	/**
	 * Validates the user input in this panel
	 *
	 * @return Whether or not this panel's input is valid
	 */
	public boolean validateInput(int numberOfPlayers){
		System.out.println("validateInput");
		boolean isValid = true;
		ArrayList<String> names = new ArrayList<String>();
		ArrayList<Color> colors = new ArrayList<Color>();
		for(int i = 1; i <= numberOfPlayers; i++){
			JComponent[] row = playerData[i];
			String playerName = (((JTextField)row[1]).getText()).trim();

			System.out.println("Name["+i+"]: [" + playerName + "], " + playerName.length());

			//player names cannot be empty after trimming
			if(playerName.isEmpty()){
				JOptionPane.showMessageDialog(null, "ERROR: Player names cannot be empty or all spaces", "ERROR", JOptionPane.ERROR_MESSAGE);
				isValid = false;
				break;
			}//end if
			//player's can't have names > 15 chars
			if(playerName.length() > 15){
				JOptionPane.showMessageDialog(null, "ERROR: Player names cannot exceed 15 characters", "ERROR", JOptionPane.ERROR_MESSAGE);
				isValid = false;
				break;
			}//end if
			//players can only have alphanumeric and spaces
			if(!playerName.matches("[A-Za-z0-9 ]*")){
				JOptionPane.showMessageDialog(null, "ERROR: Player names can only contain alphanumeric values and spaces", "ERROR", JOptionPane.ERROR_MESSAGE);
				isValid = false;
				break;
			}//end if
			//player names must be unique
			if(names.contains(playerName)){
				JOptionPane.showMessageDialog(null, "ERROR: Player names must be unique", "ERROR", JOptionPane.ERROR_MESSAGE);
				isValid = false;
				break;
			}//end if
			else{
				//it's not in the arraylist of strings yet, so add it
				names.add(playerName);
			}//end else

			//grab this player's desired color
			Color c = row[3].getBackground();
			//go through the player's color array
			for(Color color : colors){
				//graph the RGB values of all the known player colors.
				//  we then calculate the distance between all the knownn
				//  colors.  We use the distance between these points to determine
				//  if colors are too similar or not
				System.out.println(distance(color, c));
				if(distance(color, c) <= MiddleConfigPanel.COLOR_PROXIMITY_CONST){
					JOptionPane.showMessageDialog(null, "ERROR: Player colors are too similar; Try picking more unique colors", "ERROR", JOptionPane.ERROR_MESSAGE);
					isValid = false;
					break;
				}//end if
			}//end for
			//we've compared this color, now add it to the known colors array
			colors.add(c);
			System.out.println("--------");
		}//end for
		System.out.println("/validateInput");
		return isValid;
	}//end method validate

}//end class MiddleConfigPanel
